package attribute;

import item.*;
import exceptions.*;

import java.util.*;

import be.kuleuven.cs.som.annotate.*;

/**
 * @invar The items associated with each position must be proper items on position for that position.
 * 			|hasProperItemsOnPosition()
 * 
 * @invar Each position must have exactly one valid board.
 * 			|isValidBoard(board) && hasAsBoard(board)
 * 
 * @invar 	...
 * 			|	if (this.listOfItemsOnPosition.isEmpty()) then 
 * 			|		this.isTerminated() == true
 * 			|	else
 * 			| 		this.isTerminated() == false
 * 
 *
 */
/**
 * @author John Breyssem	1Ba Informatica		r0303394
 * 
 * @author Lars Feyaerts	1Ba Informatica		r0296831
 * 
 * @version	1.3
 *
 */
public class Position {
	private long X;
	private long Y;
	private Board board;
	private boolean isTerminated;
	public ArrayList<Item> listOfItemsOnPosition = new ArrayList<Item>();
	
	/**
	 * Initialise a new position with the given X, Y and board.
	 * 
	 * @param x
	 * 			The X coordinate to be appointed
	 * @param y
	 * 			The Y coordinate to be appointed
	 * @param board
	 * 			The board to be appointed.
	 * @post ...
	 * 			|new.getX() == x
	 * 			|new.getY() == y
	 * 			|new.getBoard() == board
	 * @throws IllegalArgumentException
	 * 			...
	 * 			|!this.isValidBoard(board)
	 */
	public Position(long x, long y, Board board) throws IllegalArgumentException{
		try{
			if(!this.isValidBoard(board))
				throw new IllegalArgumentException();
			this.X = x;
			this.Y = y;
			this.board = board;
		}
		catch(IllegalArgumentException exc){
			System.out.println("This is not a valid board for a position");
		}
	}
	
	/**
	 * Checks whether this position as an item of the given class.
	 * 
	 * @param item
	 * 			The item's class
	 * @return...
	 * 			|for each part on listOfItemsOnPosition:
	 * 			|	if(part.getClass() == item
	 * 			|		result == true
	 */
	public boolean hasItemOfClass(Class<? extends Item> item){
		for(Item part: this.listOfItemsOnPosition){
			if(part.getClass() == item)
				return true;
		}
		return false;
	}
	
	/**
	 * 
	 * Returns the X-coordinate of this item.
	 * 
	 * @return
	 * 		Returns the X-coordinate of this item.
	 */
	@Basic
	public long getX() throws IllegalStateException{
		if(this == null)
			throw new IllegalStateException("The item has no position");
		else
			return this.X;
	}
	
	/**
	 * Returns the Y-coordinate of this item.
	 * 
	 * @return
	 * 		Returns the Y-coordinate of this item.
	 */
	@Basic
	public long getY() throws IllegalStateException{
		if(this == null)
			throw new IllegalStateException("The item has no position");
		else
			return this.Y;
	}
	
	/**
	 * Returns the board this position is on. 
	 */
	@Basic
	public Board getBoard(){
		return this.board;
	}
	
	/**
	 * Sets the board of this position to the given board.
	 * 
	 * @param board
	 * 			The board to be set.
	 * @post ...
	 * 			|	new.getBoard() == board
	 */
	public void setBoard(Board board){
			this.board = board;
	}
	
	/**
	 * Checks whether the given board is a valid board.
	 * 
	 * @param board
	 * 			The board to check.
	 * @return ...
	 * 			|if(this.isTerminated()
	 * 			|	result == board == null
	 * 			|else
	 * 			|	result == ((board != null) && (! board.isTerminated()))
	 */
	public boolean isValidBoard(Board board){
		if(this.isTerminated())
			return board == null;
		else
			return ( (board != null) && (! board.isTerminated()));
	}
	
	/**
	 * Check whether this position has the given board as its board.
	 * 
	 * @param board
	 * 			The board to check	
	 */
	public boolean hasAsBoard(Board board){
		return this.getBoard() == board;
	}

	/**
	 * Terminates this position and all items on it.
	 * 
	 * @post ...
	 * 			|for each item in listOfItemsOnPosition:
	 * 			|	item.new.isTerminated() == true
	 * 			|!this.getBoard().getListOgPositions().contains(this)
	 * 			|this.getBoard() == null
	 * 			|this.isTerminated() == true
	 */
	public void terminate(){
		if(! this.isTerminated()){
			for(Item item: this.getListOfItemsAtPosition()) //als de list leeg is, wordt for dan uitgevoerd? Zo ja, verwijder for (oneindige lus met item.terminate()
				item.terminate();
			this.getBoard().getListOfPositions().remove(this);
			this.board = null;
			this.setTerminated(true);
		}
	}

	/**
	 * @return the isTerminated
	 */
	public boolean isTerminated() {
		return isTerminated;
	}

	/**
	 * @param isTerminated the isTerminated to set
	 */
	public void setTerminated(boolean isTerminated) {
		this.isTerminated = isTerminated;
	}
	
	/**
	 * Returns an array of items at the given position.
	 * 
	 * @param position
	 * 			The position the item are at
	 * 
	 * @return  ...
	 * 			|if(this != null && position != null) then
	 * 			|	return postition.listOfItemsOnPosition
	 * 
	 * @throws IllegalArgumentException
	 * 			...		
	 * 			|position == null
	 * @throws TerminatedObjectException
	 * 			...			
	 * 			|position.isTerminated() || this.isTerminated()
	 */
	public ArrayList<Item> getListOfItemsAtPosition() throws IllegalArgumentException, TerminatedObjectException{
		if(this == null)
			throw new IllegalArgumentException();
		if(this.isTerminated())
			throw new TerminatedObjectException(this);
		if(this.isTerminated())
			throw new TerminatedObjectException(this);
		if(this != null)
			return this.listOfItemsOnPosition;
		else
			return null;		
	}
	
	/**
	 * Check whether this position can have the given Pickable item as one of its itemsOnPosition
	 * 
	 * @param item
	 * 			The item to check.
	 * @return ...
	 * 			|result == ( (item != null) && (!this.isTerminated()) 
	 *			|	&& (!item.isTerminated()) && item.canPutItemAt(this))
	 */
	@Raw
	public boolean canHaveAsItemOnPosition(Item item){
		return ( (item != null) && (!this.isTerminated()) 
				&& (!item.isTerminated()) && item.canPutItemAt(this));
	}
	
	/**
	 * Check whether this position has proper items on position associated with it.
	 * 
	 * @return ...
	 * 			|result ==
	 * 			| 	for each item in getListOfItemsOnPosition():
	 * 			|		this.canHaveAsItemOnPosiiton(item)
	 */
	@Raw @Model
	public boolean hasProperItemsOnPosition(){
		for(Item item : this.getListOfItemsAtPosition())
			if(!this.canHaveAsItemOnPosition(item))
				return false;
		return true;
	}
}
